﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.Text;
using System.Threading;
using System.Xml;
using Common.Utilities;
using Common.Utilities.Dispatcher;

namespace Workflows.Components.TextMining.EnumerationObjects
{
    /// <summary>
    /// 
    /// </summary>
    public class TestTableDataExtractionWorker:IWorker
    {
        #region fields
        private Thread _WorkerThread;
        private Guid _JobTicketID;
        private TestTableDataExtractionInstruction _Instruction;
        private TestTableDataExtractionOutput _Output;
        private RunContext _Context;
        private string _WorkflowName;
        private string _ComponentName;

        /// <summary>
        /// 
        /// </summary>
        public string ComponentName
        {
            get { return _ComponentName; }
        }

        /// <summary>
        /// 
        /// </summary>
        public string WorkflowName
        {
            get { return _WorkflowName; }
        }
        #endregion

        #region IWorker Members

        /// <summary>
        /// handler to worker thread
        /// </summary>
        public Thread WorkerThread
        {
            get { return _WorkerThread; }
        }

        /// <summary>
        /// allow instruction to be changed at runtime
        /// </summary>
        public InstructionBase Instruction
        {
            get { return _Instruction; }
        }

        /// <summary>
        /// output
        /// </summary>
        public ResultBase Output
        {
            get { return _Output; }
        }

        /// <summary>
        /// allow worker to be created based on component type
        /// </summary>
        public Type ComponentType
        {
            get { return typeof(TestTableDataExtractionComponent); }
        }

        /// <summary>
        /// runtime context retrieved from appl domain
        /// </summary>
        public RunContext Context
        {
            get { return this._Context; }
            set { this._Context = value; }
        }

        /// <summary>
        /// this must be called before actual work
        /// </summary>
        /// <param name="ticketGuid"></param>
        /// <param name="request"></param>
        public void SetInstruction(Guid ticketGuid, InstructionBase request, string workflowName, string componentName)
        {
            this._JobTicketID = ticketGuid;
            this._Instruction = (TestTableDataExtractionInstruction)request;
            this._WorkflowName = workflowName;
            this._ComponentName = componentName;
        }

        /// <summary>
        /// this is called within subflow, where runtime context is also initialized
        /// </summary>
        /// <param name="context"></param>
        /// <param name="ticketGuid"></param>
        /// <param name="request"></param>
        public void SetInstruction(RunContext context, Guid ticketGuid, InstructionBase request, string workflowName, string componentName)
        {
            this._JobTicketID = ticketGuid;
            this._Instruction = (TestTableDataExtractionInstruction)request;
            this._Context = context;
            this._WorkflowName = workflowName;
            this._ComponentName = componentName;
        }

        /// <summary>
        /// this is called synchronously, used in testing mode
        /// </summary>
        public void DoWork()
        {
            if (this.WorkerStartedEvent != null)
            {
                this.WorkerStartedEvent(this._JobTicketID, this, this.GetType(), this._Instruction);
            }
            this._WorkerThread = Thread.CurrentThread;
            this.EvaluatePerformance();
        }


        /// <summary>
        /// this is called by job dispatcher
        /// </summary>
        public void DoWorkAsync()
        {
            if (this.WorkerStartedEvent != null)
            {
                this.WorkerStartedEvent(this._JobTicketID, this, this.GetType(), this._Instruction);
            }
            this._WorkerThread = new Thread(new ThreadStart(this.EvaluatePerformance));
            this._WorkerThread.Start();
        }

        /// <summary>
        /// this is called by job monitor/manager
        /// </summary>
        public void CancelWork()
        {
            if (this._WorkerThread != null && this._WorkerThread.IsAlive)
            {
                this._WorkerThread.Abort();
                if (this.WorkerCancelledEvent != null)
                {
                    this.WorkerCancelledEvent(this._JobTicketID, this.GetType(), this._Instruction);
                }
            }
        }

        public event WorkerStarted WorkerStartedEvent;
        public event WorkerFinished WorkerFinishedEvent;
        public event WorkerHasError WorkerErredEvent;
        public event WorkerCancelled WorkerCancelledEvent;
        public event WorkerStatusChanged WorkerStatusChangedEvent;

        #endregion

        #region work
        private void EvaluatePerformance()
        {
            try
            {
                // doc list
                int totalDocCount = 0;
                int docMissed = 0;
                int docExtra = 0;
                int docDuplicated = 0;
                this.EvaluateDocListPerformance(
                    this._Instruction.OurDocListResultFilePath,
                    ref totalDocCount, ref docMissed, ref docExtra, ref docDuplicated);

                // table scan
                int totalTableOccuranceCount = 0;
                int tableScanMissed=0;
                int tableScanExtra=0;
                this.EvaluateTableScanPerformance(
                    this._Instruction.OurTableScanResultFilePath,
                    ref totalTableOccuranceCount, ref tableScanMissed, ref tableScanExtra);

                // table layout
                int totalTableLayoutCount = 0;
                int scannedTableLayoutFailed = 0;
                int electronicTableLayoutFailed = 0;
                this.EvaluateTableLayoutPerformance(
                    this._Instruction.OurTableLayoutResultFilePath,
                    ref totalTableLayoutCount, ref scannedTableLayoutFailed, ref electronicTableLayoutFailed);

                // table extract
                int totalTableExtractCount = 0;
                int tableExtractMissed = 0;
                int tableExtractWithFewerRecords = 0;
                this.EvaluateTableExtractPerformance(
                    this._Instruction.OurTableExtractResultFolderPath,
                    ref totalTableExtractCount, ref tableExtractMissed, ref tableExtractWithFewerRecords);

                this._Output =
                    new TestTableDataExtractionOutput(
                        this._Instruction.OwnerComponentID, this._JobTicketID,
                        totalDocCount, docMissed, docExtra, docDuplicated,
                        this._Instruction.DocListPerformanceResultFilePath,
                        totalTableOccuranceCount, tableScanMissed, tableScanExtra,
                        this._Instruction.TableScanPerformanceResultFilePath,
                        totalTableLayoutCount, scannedTableLayoutFailed, electronicTableLayoutFailed,
                        this._Instruction.TableLayoutPerformanceResultFilePath,
                        totalTableExtractCount, tableExtractMissed, tableExtractWithFewerRecords,
                        this._Instruction.TableExtractPerformanceResultFilePath);

                if(this.WorkerFinishedEvent !=null)
                {
                    this.WorkerFinishedEvent(this._JobTicketID, this.GetType(), this._Instruction, this._Output);
                }
            }
            catch (Exception ex)
            {
                if (this.WorkerErredEvent != null)
                {
                    this.WorkerErredEvent(this._JobTicketID, this.GetType(), this._Instruction, ex.ToString(),true);
                }
            }
        }
        #endregion

        #region doc list
        private void EvaluateDocListPerformance(
            string ourDocListResultFilePath, 
            ref int totalDocCount, 
            ref int docMissed,
            ref int docExtra, 
            ref int docDuplicated)
        {
            List<int> expectedDocIDs = new List<int>();
            SqlConnection conn=new SqlConnection(this._Instruction.CorrectResultConnStr);
            try
            {
                conn.Open();
                SqlCommand cmd = conn.CreateCommand();
                cmd.CommandText = string.Format("select {0} from ({1}) temp",
                                                this._Instruction.CorrectResultDocIDField,
                                                this._Instruction.CorrectResultSelectSql);
                cmd.CommandType = CommandType.Text;
                SqlDataReader reader = cmd.ExecuteReader();
                while (reader.Read())
                {
                    int docID = int.Parse(reader[this._Instruction.CorrectResultDocIDField].ToString());
                    if(!expectedDocIDs.Contains(docID))
                    {
                        expectedDocIDs.Add(docID);
                    }
                }
                reader.Close();
            }
            catch (Exception ex)
            {
                if(this.WorkerErredEvent !=null)
                {
                    this.WorkerErredEvent(this._JobTicketID, this.GetType(), this._Instruction, ex.ToString(),true);  
                }
            }
            finally
            {
                conn.Close();
            }

            List<int> ourDocIDs = this.ReadDocListResult(ourDocListResultFilePath);
            totalDocCount = ourDocIDs.Count;
            docMissed = 0;
            docExtra = 0;
            docDuplicated = 0;
            foreach(int docID1 in expectedDocIDs)
            {
                if(!ourDocIDs.Contains(docID1))
                {
                    docMissed++;
                }
            }
            foreach(int docID2 in ourDocIDs)
            {
                if(!expectedDocIDs.Contains(docID2))
                {
                    docExtra++;
                }
            }
            if(totalDocCount + docMissed - docExtra > expectedDocIDs.Count)
            {
                docDuplicated = totalDocCount + docMissed - docExtra - expectedDocIDs.Count;
            }
        }

        private List<int> ReadDocListResult(string docListResultFilePath)
        {
            List<int> docIDs = new List<int>();
            XmlDocument xDoc = new XmlDocument();
            xDoc.Load(docListResultFilePath);
            XmlNodeList fileNodes = xDoc.SelectNodes("//File");
            foreach(XmlNode fileNode in fileNodes)
            {
                int docID = int.Parse(fileNode.Attributes.GetNamedItem("DocID").Value);
                docIDs.Add(docID);
            }
            return docIDs;
        }
        #endregion

        #region table scan
        private void EvaluateTableScanPerformance(
            string ourTableScanResultFilePath,
            ref int totalTableOccurance,
            ref int tableScanMissed,
            ref int tableScanExtra)
        {
            Dictionary<int, List<int>> expectedFilePagesContainingTable = new Dictionary<int, List<int>>();
            SqlConnection conn = new SqlConnection(this._Instruction.CorrectResultConnStr);
            try
            {
                conn.Open();
                SqlCommand cmd = conn.CreateCommand();
                cmd.CommandText = string.Format("select {0},{1} from ({2}) tmp",
                                                this._Instruction.CorrectResultDocIDField,
                                                this._Instruction.CorrectResultPageNumberField,
                                                this._Instruction.CorrectResultSelectSql);
                SqlDataReader reader = cmd.ExecuteReader();
                while(reader.Read())
                {
                    int docID = (int) reader[this._Instruction.CorrectResultDocIDField];
                    int pageNum = 0;
                    if( reader[this._Instruction.CorrectResultPageNumberField]!=null)
                    {
                        pageNum = (int)reader[this._Instruction.CorrectResultPageNumberField];
                    }
                    if(expectedFilePagesContainingTable.ContainsKey(docID))
                    {
                        List<int> pageNums = expectedFilePagesContainingTable[docID];
                        pageNums.Add(pageNum);
                        expectedFilePagesContainingTable[docID] = pageNums;
                    }
                    else
                    {
                        List<int> pageNums = new List<int>();
                        pageNums.Add(pageNum);
                        expectedFilePagesContainingTable.Add(docID, pageNums);
                    }
                }
                reader.Close();
            }
            catch (Exception ex)
            {
                if(this.WorkerErredEvent !=null)
                {
                    this.WorkerErredEvent(this._JobTicketID, this.GetType(), this._Instruction, ex.ToString(),true);
                }
            }
            finally
            {
                conn.Close();
            }

            Dictionary<int, List<int>> filePages = this.ReadTableScanResult(ourTableScanResultFilePath);
            totalTableOccurance = 0;
            tableScanExtra = 0;
            tableScanMissed = 0;

            foreach(int docID in filePages.Keys)
            {
                List<int> pageNums2 = filePages[docID];
                totalTableOccurance += pageNums2.Count;
                if(expectedFilePagesContainingTable.ContainsKey(docID))
                {
                    List<int> pageNums1 = expectedFilePagesContainingTable[docID];
                    foreach(int pageNum2 in pageNums2)
                    {
                        if(!pageNums1.Contains(pageNum2))
                        {
                            tableScanExtra++;
                        }
                    }
                    foreach(int pageNum1 in pageNums1)
                    {
                        if(!pageNums2.Contains(pageNum1))
                        {
                            tableScanMissed++;
                        }
                    }
                }
                else
                {
                    tableScanExtra += pageNums2.Count;
                }
            }
            foreach(int docID in expectedFilePagesContainingTable.Keys)
            {
                if(!filePages.ContainsKey(docID))
                {
                    List<int> pageNums = expectedFilePagesContainingTable[docID];
                    tableScanMissed += pageNums.Count;
                }
            }
        }

        private Dictionary<int, List<int>> ReadTableScanResult(string tableScanResultFilePath)
        {
            Dictionary<int, List<int>> filePages = new Dictionary<int, List<int>>();
            XmlDocument xDoc = new XmlDocument();
            xDoc.Load(tableScanResultFilePath);
            XmlNodeList fileNodes = xDoc.SelectNodes("//File");
            foreach(XmlNode fileNode in fileNodes)
            {
                int docID = int.Parse(fileNode.Attributes.GetNamedItem("DocID").Value);
                int[] pages = IntUtil.ToArray(fileNode.Attributes.GetNamedItem("Pages").Value);
                List<int> pageNumbers = new List<int>();
                if(pages !=null && pages.Length>0)
                {
                    pageNumbers.AddRange(pages);
                }
                filePages.Add(docID, pageNumbers);
            }
            return filePages;
        }
        #endregion

        #region table layout
        private void EvaluateTableLayoutPerformance(
            string ourTableLayoutResultFilePath,
            ref int totalTableLayoutCount,
            ref int tableLayoutMissed,
            ref int tableLayoutExtra)
        {
            Dictionary<int, Dictionary<int, List<string>>> expectedFilePageColumns =
                new Dictionary<int, Dictionary<int, List<string>>>();
            TableColumn[] cols =
                DbUtil.GetTableColumns(
                    DataProviderType.MSSQL, 
                    this._Instruction.CorrectResultConnStr,
                    this._Instruction.CorrectResultSelectSql);
            SqlConnection conn = new SqlConnection(this._Instruction.CorrectResultConnStr);
            try
            {
                conn.Open();
                SqlCommand cmd = conn.CreateCommand();
                cmd.CommandText = this._Instruction.CorrectResultSelectSql;
                cmd.CommandType = CommandType.Text;
                SqlDataReader reader = cmd.ExecuteReader();
                while(reader.Read())
                {
                    int docID = (int) reader[this._Instruction.CorrectResultDocIDField];
                    Dictionary<int, List<string>> pageColumns = new Dictionary<int, List<string>>();
                    if(expectedFilePageColumns.ContainsKey(docID))
                    {
                        pageColumns = expectedFilePageColumns[docID];
                    }
                    if (reader[this._Instruction.CorrectResultPageNumberField] != null)
                    {
                        int pageNum = 0;
                        if(reader[this._Instruction.CorrectResultPageNumberField]!=null)
                        {
                            pageNum = (int)reader[this._Instruction.CorrectResultPageNumberField];
                        }
                        List<string> colHeaders = new List<string>();
                        if(pageColumns.ContainsKey(pageNum))
                        {
                            colHeaders = pageColumns[pageNum];
                        }
                        foreach(TableColumn col in cols)
                        {
                            if(reader[col.ColumnName]!=null && reader[col.ColumnName].ToString().Length>0)
                            {
                                if(!colHeaders.Contains(col.ColumnName))
                                {
                                    colHeaders.Add(col.ColumnName);
                                }
                            }
                        }
                        if(pageColumns.ContainsKey(pageNum))
                        {
                            pageColumns[pageNum] = colHeaders;
                        }
                        else
                        {
                            pageColumns.Add(pageNum, colHeaders);
                        }
                    }
                    if(expectedFilePageColumns.ContainsKey(docID))
                    {
                        expectedFilePageColumns[docID] = pageColumns;
                    }
                    else
                    {
                        expectedFilePageColumns.Add(docID, pageColumns);
                    }
                }
                reader.Close();
            }
            catch (Exception ex)
            {
                if(this.WorkerErredEvent !=null )
                {
                    this.WorkerErredEvent(this._JobTicketID, this.GetType(), this._Instruction, ex.ToString(),true);
                }
            }
            finally
            {
                conn.Close();
            }

            Dictionary<int, Dictionary<int, List<string>>> ourFilePageCols =
                this.ReadTableLayoutResult(ourTableLayoutResultFilePath);
            totalTableLayoutCount = 0;
            tableLayoutExtra = 0;
            tableLayoutMissed = 0;
            foreach(int docID2 in ourFilePageCols.Keys)
            {
                Dictionary<int, List<string>> pageCols2 = ourFilePageCols[docID2];
                totalTableLayoutCount += pageCols2.Count;
                if(!expectedFilePageColumns.ContainsKey(docID2))
                {
                    tableLayoutExtra += pageCols2.Count;
                }
                else
                {
                    Dictionary<int, List<string>> pageCols1 = expectedFilePageColumns[docID2];
                    foreach(int pageNum2 in pageCols2.Keys)
                    {
                        if(pageCols1.ContainsKey(pageNum2))
                        {
                            // check column header match?
                        }
                        else
                        {
                            tableLayoutExtra += 1;
                        }
                    }
                    foreach(int pageNum1 in pageCols1.Keys)
                    {
                        if(!pageCols2.ContainsKey(pageNum1))
                        {
                            tableLayoutMissed += 1;
                        }
                    }
                }
            }

            foreach(int docID in expectedFilePageColumns.Keys)
            {
                Dictionary<int, List<string>> pageCols1 = expectedFilePageColumns[docID];
                if(!ourFilePageCols.ContainsKey(docID))
                {
                    tableLayoutMissed += pageCols1.Count;
                }
            }
        }

        private Dictionary<int,Dictionary<int,List<string>>> ReadTableLayoutResult(string tableLayoutResultFilePath)
        {
            Dictionary<int, Dictionary<int, List<string>>> filePageLayouts =
                new Dictionary<int, Dictionary<int, List<string>>>();
            XmlDocument xDoc = new XmlDocument();
            xDoc.Load(tableLayoutResultFilePath);
            XmlNodeList fileNodes = xDoc.SelectNodes("//File");

            foreach(XmlNode fileNode in fileNodes)
            {
                int docID = int.Parse(fileNode.Attributes.GetNamedItem("DocID").Value);
                XmlNodeList pageNodes = fileNode.SelectNodes("Page");
                Dictionary<int, List<string>> pageCols = new Dictionary<int, List<string>>();
                foreach(XmlNode pageNode in pageNodes)
                {
                    int pageNum = int.Parse(pageNode.Attributes.GetNamedItem("PageNumber").Value);
                    XmlNodeList colHeaderNodes = pageNode.SelectNodes("ColumnHeader");
                    List<string> colHeaders = new List<string>();
                    foreach(XmlNode colNode in colHeaderNodes)
                    {
                        string colHeader = colNode.Attributes.GetNamedItem("Name").Value;
                        colHeaders.Add(colHeader);
                    }
                    pageCols.Add(pageNum, colHeaders);
                }

                filePageLayouts.Add(docID, pageCols);
            }

            return filePageLayouts;
        }
        #endregion

        #region table extract
        private void EvaluateTableExtractPerformance(
            string ourTableExtractResultFilePath,
            ref int totalTableExtractCount,
            ref int tableExtractFailed,
            ref int tableExtractWithFewerRecords)
        {
            Dictionary<int, Dictionary<int, DataTable>> fileData = new Dictionary<int, Dictionary<int, DataTable>>();
            SqlConnection conn = new SqlConnection(this._Instruction.CorrectResultConnStr);
            TableColumn[] cols =
                DbUtil.GetTableColumns(
                    DataProviderType.MSSQL,
                    this._Instruction.CorrectResultConnStr,
                    this._Instruction.CorrectResultSelectSql);

            try
            {
                conn.Open();
                SqlCommand cmd = conn.CreateCommand();
                cmd.CommandType = CommandType.Text;
                cmd.CommandText = this._Instruction.CorrectResultSelectSql;
                SqlDataReader reader = cmd.ExecuteReader();
                while(reader.Read())
                {
                    int docID = int.Parse(reader[this._Instruction.CorrectResultDocIDField].ToString());
                    Dictionary<int, DataTable> pageData = new Dictionary<int, DataTable>();
                    if(fileData.ContainsKey(docID))
                    {
                        pageData = fileData[docID];
                    }

                    int pageNum = 0;
                    if(reader[this._Instruction.CorrectResultPageNumberField]!=null)
                    {
                        pageNum = int.Parse(reader[this._Instruction.CorrectResultPageNumberField].ToString());
                    }
                    DataTable dtData = new DataTable();
                    if(pageData.ContainsKey(pageNum))
                    {
                        dtData = pageData[pageNum];
                    }
                    else
                    {
                        foreach(TableColumn col in cols)
                        {
                            dtData.Columns.Add(col.ColumnName, FieldDataType.ToSystemType(col.DbType));
                        }
                    }

                    DataRow dr = dtData.NewRow();
                    foreach(TableColumn col in cols)
                    {
                        dr[col.ColumnName] = reader[col.ColumnName];
                    }
                    dtData.Rows.Add(dr);

                    if(pageData.ContainsKey(pageNum))
                    {
                        pageData[pageNum] = dtData;
                    }
                    else
                    {
                        pageData.Add(pageNum, dtData);
                    }
                    if(fileData.ContainsKey(docID))
                    {
                        fileData[docID] = pageData;
                    }
                    else
                    {
                        fileData.Add(docID, pageData);
                    }
                }
                reader.Close();
            }
            catch (Exception ex)
            {
                if(this.WorkerErredEvent !=null)
                {
                    this.WorkerErredEvent(this._JobTicketID, this.GetType(), this._Instruction, ex.ToString(),true);
                }
            }
            finally
            {
                conn.Close();
            }

            Dictionary<int, Dictionary<int, DataTable>> ourTableExtractResults =
                this.ReadTableExtractResult(ourTableExtractResultFilePath, 
                this._Instruction.SrcTgtFieldMappings);

            totalTableExtractCount = 0;
            tableExtractFailed = 0;
            tableExtractWithFewerRecords = 0;

            foreach(int docID2 in ourTableExtractResults.Keys)
            {
                Dictionary<int, DataTable> pageData2 = ourTableExtractResults[docID2];
                totalTableExtractCount += pageData2.Count;
                if(!fileData.ContainsKey(docID2))
                {
                    // extra table??
                }
                else
                {
                    Dictionary<int, DataTable> pageData1 = fileData[docID2];

                    foreach(int page1 in pageData1.Keys)
                    {
                        if(!pageData2.ContainsKey(page1))
                        {
                            tableExtractFailed += 1;
                        }
                        else
                        {
                            DataTable dtData2 = pageData2[page1];
                            DataTable dtData1 = pageData1[page1];
                            if(dtData1.Rows.Count > dtData2.Rows.Count )
                            {
                                tableExtractWithFewerRecords += 1;
                            }
                        }
                    }
                }
            }

            foreach(int docID1 in fileData.Keys)
            {
                if(!ourTableExtractResults.ContainsKey(docID1))
                {
                    Dictionary<int, DataTable> pageData1 = fileData[docID1];
                    tableExtractFailed += pageData1.Count;
                }
            }
        }

        /// <summary>
        /// field mapping key must be all upper case
        /// </summary>
        /// <param name="tableExtractFolderPath"></param>
        /// <param name="srcTgtFieldMappings"></param>
        /// <returns></returns>
        private Dictionary<int, Dictionary<int,DataTable>> ReadTableExtractResult(string tableExtractFolderPath, Dictionary<string, string> srcTgtFieldMappings)
        {
            Dictionary<int, Dictionary<int, DataTable>> filePageData =
                new Dictionary<int, Dictionary<int, DataTable>>();
            DataTable dtTamplate = new DataTable();
            foreach (string srcField in srcTgtFieldMappings.Keys)
            {
                string tgtField = srcTgtFieldMappings[srcField];
                dtTamplate.Columns.Add(tgtField, typeof(string));
            }

            DirectoryInfo dirInfo = new DirectoryInfo(tableExtractFolderPath);
            FileInfo[] xmlFiles = dirInfo.GetFiles("*.xml", SearchOption.TopDirectoryOnly);
            foreach(FileInfo xmlFile in xmlFiles)
            {
                XmlDocument xDoc = new XmlDocument();
                xDoc.Load(xmlFile.FullName);
                XmlNode fileNode = xDoc.DocumentElement;
                int docID = int.Parse(fileNode.Attributes.GetNamedItem("DocID").Value);
                int pageNum = int.Parse(fileNode.Attributes.GetNamedItem("PageNumber").Value);
                DataTable dtData = dtTamplate.Clone();
                List<string> srcCols = new List<string>();
                XmlNodeList columnNodes = fileNode.SelectNodes("Columns/Column");
                foreach(XmlNode columnNode in columnNodes)
                {
                    string colName = columnNode.Attributes.GetNamedItem("ColumnName").Value;
                    srcCols.Add(colName);
                }
                XmlNodeList rowNodes = fileNode.SelectNodes("Rows/Row");
                foreach (XmlNode rowNode in rowNodes)
                {
                    DataRow dr = dtData.NewRow();
                    XmlNodeList cellNodes = rowNode.ChildNodes;
                    if (cellNodes != null && cellNodes.Count == columnNodes.Count)
                    {
                        for(int i=0;i<cellNodes.Count; i++)
                        {
                            if(srcTgtFieldMappings.ContainsKey(srcCols[i].ToUpper()))
                            {
                                string tgtColName = srcTgtFieldMappings[srcCols[i].ToUpper()];
                                dr[tgtColName] = cellNodes[i].InnerText;
                            }
                        }
                        dtData.Rows.Add(dr);
                    }
                }

                Dictionary<int, DataTable> pageData = new Dictionary<int, DataTable>();
                if(filePageData.ContainsKey(docID))
                {
                    pageData = filePageData[docID];
                }
                if(pageData.ContainsKey(pageNum))
                {
                    pageData[pageNum] = dtData;
                }
                else
                {
                    pageData.Add(pageNum, dtData);
                }
                if (filePageData.ContainsKey(docID))
                {
                    filePageData[docID] = pageData;
                }
                else
                {
                    filePageData.Add(docID, pageData);
                }
            }

            return filePageData;
        }
        #endregion
    }
}
